home *** CD-ROM | disk | FTP | other *** search
- From pgoujard@infocom.co.uk Wed Aug 11 23:32:43 1993
- Path: uunet!cs.utexas.edu!math.ohio-state.edu!magnus.acs.ohio-state.edu!usenet.ins.cwru.edu!agate!doc.ic.ac.uk!uknet!infocom.co.uk!pgoujard
- From: pgoujard@infocom.co.uk (Philippe Goujard)
- Newsgroups: alt.sources
- Subject: SLNR v1.2.c an offline newsreader 2/3
- Message-ID: <21529@infocom.co.uk>
- Date: 12 Aug 93 00:23:23 GMT
- Followup-To: alt.usenet.offline-reader
- Organization: INFOCOM Public Access Unix, (ModemLine) +44 [0] 734 340055
- Lines: 2340
- X-Newsreader: TIN [version 1.1 PL9]
- Xref: uunet alt.sources:9101
-
-
-
- #!/bin/sh
- # this is slnr.sh.02 (part 2 of a multipart archive)
- # do not concatenate these parts, unpack them in order with /bin/sh
- # file slnr/slnp.fmt continued
- #
- if test ! -r _shar_seq_.tmp; then
- echo 'Please unpack part 1 first!'
- exit 1
- fi
- (read Scheck
- if test "$Scheck" != 2; then
- echo Please unpack part "$Scheck" next!
- exit 1
- else
- exit 0
- fi
- ) < _shar_seq_.tmp || exit 1
- if test ! -f _shar_wnt_.tmp; then
- echo 'x - still skipping slnr/slnp.fmt'
- else
- echo 'x - continuing file slnr/slnp.fmt'
- sed 's/^X//' << 'SHAR_EOF' >> 'slnr/slnp.fmt' &&
- original articles, they should be converted into single spaces. All fields
- must be present, but some may be empty. The "bytes" field must not be empty,
- since it provides necessary information for packet readers. Each field must
- conform to the Internet RFC documents RFC-822 or RFC-1036.
- X
- Optionally, a header line may end with one or more extra TAB-separated fields
- for other RFC-compliant header fields, together with the header field names.
- e.g. "Supersedes: <1234@foovax>". These fields are not defined by this
- version of the specification, and are by arrangement between the generator
- and recipient only.
- X
- This format is compatible with the news overview database format of C-news.
- The only difference being the substitution of an offset for the article
- number used by C-news. The C-news format was designed to assist threading
- newsreaders, so this packet format should provide similar assistance to
- compliant packet readers.
- X
- The 'C' format is similar to 'c', except that the "mesgid" and "refs" fields
- are dropped. These fields can commonly be quite long and are mainly of use to
- packet readers which perform Message-ID based message threading. Packet
- readers which perform subject threading (i.e. sort on the subject line and
- then on the date and/or arrival order) do not require such information. The
- format of the header lines in this case is as follows:
- X
- offset<TAB>subject<TAB>author<TAB>date<TAB>bytes<TAB>lines
- X
- Further TAB-separated fields may be added in future versions of this
- specification.
- X
- The "author" field is slightly different to the 'c' format. Instead of
- an RFC-822 format address, it is just the author's name, extracted from the
- "From:" line of the message. Most RFC-822 and RFC-1036 "From:" lines have one
- of the following forms:
- X
- X address
- X address (name)
- X name <address>
- X
- Names may sometimes be surrounded by double-quote characters, have embedded
- "(...)" sequences, or contain "useless" information after a comma (",") or
- slash ("/"). The main requirement is that the generating software produce
- some kind of (more or less) meaningful string for the name of the author which
- can be displayed to the user by a packet reader. See RFC-822 and RFC-1036
- for more information on the syntax of the "From:" line in messages.
- X
- The 'i' index format is purely binary, using 8 bytes for each message in the
- corresponding message file. The first 4 bytes specify the offset into the
- message file of the message and the remaining 4 bytes specify the number of
- bytes in the message. Each 4-byte quantity is stored in big-endian order
- (high byte first). This format is supplied to provide a trade-off between
- transmission time and easy extraction of messages from a message file.
- X
- It is recommended that packet generators support at least the 'C' format, and
- that packet readers support at least 'c', 'C' and 'i'. If a type is
- unrecognised by a packet reader, then it must "pull apart" the message file
- into separate messages itself, or flag the message area as unparsable by the
- packet reader.
- X
- X
- 2.5 ) REPLIES File
- ~~~~~~~~~~~~~~~~~~
- X
- One of the remaining requirements is a mechanism for a user to upload replies
- or new messages to a generating system for mailing or posting. While it is
- possible to re-use the AREAS file for this purpose, keeping the download and
- upload sections separate will help prevent messages being fed back into a
- network erroneously.
- X
- The REPLIES file has a similar format to the AREAS file. Each line has the
- following form:
- X
- prefix<TAB>reply kind<TAB>type[<TAB>message_no]
- X
- The "prefix" and "type" fields are as before. The "reply kind" field indicates
-
- the mechanism to use when transmitting the messages in the message file. The
- following values are currently defined:
- X
- mail : Transmit an RFC-822 compliant personal mail message
- news : Transmit an RFC-1036 compliant USENET news posting
- X
- The message_no field is optional and indicates the number of messages in
- the prefix.MSG file.
- X
- On a Unix system, transmission of mail and news is usually performed with the
- "sendmail" and "inews" programs respectively. Additional kinds may be
- specified in a future version of this specification for other message formats.
- Note: it is discouraged that the kinds "mail" and "news" be used for anything
- other than RFC-compliant messages. In particular, FidoNet or QWK messages
- should use a different reply kind. Messages of the same reply kind can be
- placed in the same message file, or in separate message files.
- X
- Further TAB-separated fields may be added to the lines in the REPLIES file
- in a future version of this specification.
- X
- It is recommended that a message file type of 'b' or 'B' be used for sending
- replies to minimise the chance of message corruption. The recommended index
- file types for replies are 'i' and 'n'. The index types 'c' and 'C' are
- discouraged because they do not provide useful information for reply purposes.
- X
- The format of the messages in the message files should follow the relevant
- RFC standards, with the following restriction: any "From:", "Sender:",
- "Control:", "Approved:" or other similar "dangerous" header lines should be
- ignored by the system transmitting the replies to prevent forgeries from
- occuring. In particular, the "From:" header should be determined from the
- user's login name, or some other similar means, rather than from any data
- supplied in the user's message.
- X
- In most cases, mail messages will contain "To:", "Subject:", "Cc:", "Bcc:"
- and "Reply-To:" header lines, and news messages will contain "Newsgroups:",
- "Subject:", "Followup-To:", "Keywords:", "Summary:" and "Reply-To:" header
- lines. Other optional headers (especially MIME content headers) may also
- be present.
- X
- The automatic addition of a signature by the transmitting system is
- discouraged. Signatures should be added by the user's message composition
- software instead, if desired.
- X
- A method for allowing replies from more than one person to be stored in the
- same packet was considered, but was rejected for security reasons.
- X
- 2.6) List File
- X
- The LIST file may be used to send a list of available message areas to the
- receiving system. Its format is similar to the AREAS file, with the prefix
- field deleted. Each line has the following form:
- X
- area name<TAB>type[<TAB>description]
- X
- where "area name" is the name of the message area, "type" is a 2 letter
- message and index type code and "description" is an optional message area
- description. Further optional fields may be added in a future version of
- this specification.
- X
- 3) Future enhancements
- ======================
- X
- The obvious enhancement that can be made is to support other message formats,
- especially FidoNet formats. Currently the message area file code 'q' is
- reserved for QWK-format messages. This will be defined in a future version
- of this specification if demand warrants.
- X
- Experimentation with other formats is encouraged, but please contact the
- author first to prevent double-ups from occurring.
- X
- X
- X
- X
- X
- Appendix A : Convention used by getnews and SLNR
- ===========
- X
- X A.1. Getnews (version 1.6)
- X ~~~~~~~~~~~~
- X Getnews creates .MSG files named "001.MSG" "002.MSG" etc...
- X Usually 001.MSG is the user mailbox.
- X Getnews does not create index files (.IDX) and sets the index type
- X field to "n" (for none). Getnews does not create the INFO or LIST file
- X either.
- X Getnews does however use the optional fields in the AREAS file to
- X store the description of the newsgroup and the number of articles.
- X
- X
- X
- X A.2 SLNR (version 1.7)
- X ~~~~~~~~
- X SLNR does not check for indexes nor INFO, COMMAND or LIST files.
- X SLNR creates reply files under the name Rxxx.MSG where "xxx" is the
- X name of the area file to which it is replying. If the files have been
- X created by getnews, SLNR will create reply files under the names
- X "R001.MSG" "R002.MSG" etc...
- X Like getnews, SLNR does not create indexes and sets the index_type
- X field to "n".
- SHAR_EOF
- echo 'File slnr/slnp.fmt is complete' &&
- chmod 0440 slnr/slnp.fmt ||
- echo 'restore of slnr/slnp.fmt failed'
- Wc_c="`wc -c < 'slnr/slnp.fmt'`"
- test 28248 -eq "$Wc_c" ||
- echo 'slnr/slnp.fmt: original size 28248, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= slnr/slnr.c ==============
- if test -f 'slnr/slnr.c' -a X"$1" != X"-c"; then
- echo 'x - skipping slnr/slnr.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting slnr/slnr.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'slnr/slnr.c' &&
- /*
- ** SLNR : Simple Local News Reader
- ** Author : P.Goujard
- ** Started : 22/01/93
- ** Version : 2.11
- ** Last modified : 11 Aug 1993
- */
- X
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <ctype.h> /* for toupper */
- #include <stdlib.h> /* For getenv */
- #include "colour.h"
- X
- #ifdef ATARI
- #include <mintbind.h>
- #include <vt52.h>
- #endif
- X
- #ifdef OS2
- #include <io.h>
- #include <conio.h>
- #endif
- X
- #ifndef ATARI
- #include <fcntl.h> /* MS-DOS *does* need fcntl.h, but not termio.h */
- #ifndef __MSDOS__
- #ifndef OS2
- #include <unistd.h>
- /*
- ** This is used for putting the terminal in raw mode
- ** Under dos we don't need that since getch() returns directly
- */
- #include <termio.h>
- #endif
- #endif
- #endif
- X
- #define NEWSLIST "AREAS"
- #define NEWSTXT ".MSG"
- #define MMDF_SEPARATOR ""
- #define MAIL_SEPARATOR "From "
- #define REWIND 1
- #define SET 1
- #define UNSET 0
- #define ASK 2
- X
- #define NO_HEADER 1L
- #define GET_SUBJECT 2L
- #define GET_ID 4L
- #define GET_FROM 8L
- #define GET_REPLYTO 16L
- X
- #define INITFILE "slnr.ini"
- X
- #ifdef __MSDOS__
- # define PAGER "more <"
- # define EDIT "q"
- # define COMPRESS "pkzip"
- # define MAXART 500 /* 1000 /* Originally 500 */
- # define MAXGRP 100 /* 500 /* Originally 100 */
- #else
- # ifdef ATARI
- X char PAGER[64];
- # define EDIT "???" /* To be defined by atari guru */
- # define COMPRESS "stzip" /* To be defined by atari guru */
- # define MAXART 1000
- # define MAXGRP 200
- # else /* Unix */
- # define PAGER "simplpg"
- # define EDIT "simpled"
- # define COMPRESS "zip"
- # define MAXART 5000
- # define MAXGRP 1000
- # endif
- #endif
- X
- /*
- ** Global variables
- */
- static char pager_str[80],edit_str[80],compress_str[80],quote_str[80];
- static int use_ansi;
- int Lines=24;
- X
- struct header{
- X char *subject;
- X char *from;
- X char *id;
- X char *replyto;
- };
- X
- /*
- ** The idea of a color_string array is to allow several scr() call in
- ** one printf.
- ** If we don't do that printf only see the last scr() since they all
- ** have the same address.
- */
- char Color_string[MAX_CLR][12];
- int Color_no = 0;
- X
- X
- /*
- ** This is not very clean : those 2 arrays should be allocated
- ** only when needed instead of reserving memory which maybe won't
- ** be used.
- */
- struct arts {
- X char from[20];
- X unsigned long size;
- X char subject[60];
- X unsigned long rank;
- X unsigned long ptr; /* Pointer to start of article */
- }
- #ifdef __MSDOS2__ /* Create huge array if MS-DOS */
- X huge article[MAXART];
- #else
- X article[MAXART];
- #endif
- X
- struct grps {
- X char prefix[10]; /* The .MSG file containing the articles */
- X char name[40];
- X char desc[80];
- X unsigned int art; /* number of articles in that group */
- X unsigned long start; /* number of 1st article in the article[] table */
- X char msg_type;
- X char idx_type;
- X unsigned int reply; /* Number of replies for that group, default 0 */
- }
- #ifdef __MSDOS2__ /* Create huge array if MS-DOS */
- X huge group[MAXGRP];
- #else
- X group[MAXGRP];
- #endif
- X
- unsigned int cur_grp=0, cur_art=0, old_cur_art=0, cur_line=0;
- int no=0;
- X
- #ifdef ATARI
- X
- static void cls (void);
- X
- int atari_res = 0;
- int atari_old_colour[16];
- X
- static void initialise_atari(void)
- {
- X int i;
- X char *pager;
- X
- X /*
- X * Check the environment to see if a suitable pager is defined.
- X * If so, make up the PAGER format string to use it. In all other
- X * cases make up a generic call to "more".
- X */
- X
- X pager = (char*)getenv("PAGER");
- X
- X if (pager==(char*)NULL) {
- X sprintf(PAGER, "more ");
- X } else {
- X sprintf(PAGER, "%s ", pager);
- X }
- X
- X /*
- X * Pick up screen resolution (used in function "scr").
- X */
- X
- X atari_res = Getrez();
- X if (atari_res==0) {
- X fprintf(stderr, "Cannot run in ST-low resolution\n");
- X exit(1);
- X };
- X
- X /*
- X * Record the original palette values for restore later
- X */
- X
- X for (i=0; i<16; i++) atari_old_colour[i] = Setcolor(i, -1);
- X
- X /*
- X * Set up the colour palette to match up colours with the
- X * numbers used in the colour.h include file.
- X */
- X
- X (void) Setcolor(1, 0x000); /* Black */
- X (void) Setcolor(2, 0x700); /* Red */
- X (void) Setcolor(3, 0x070); /* Green */
- X (void) Setcolor(4, 0x770); /* Yellow */
- X (void) Setcolor(5, 0x007); /* Blue */
- X (void) Setcolor(6, 0x707); /* Magenta */
- X (void) Setcolor(7, 0x077); /* Cyan */
- X (void) Setcolor(8, 0x777); /* White */
- X
- X /*
- X * Now clear the screen
- X */
- X
- X printf("%s\n", scr(1,WHITE, BLACK));
- X cls();
- }
- X
- static void closedown_atari(void)
- {
- X int i;
- X
- X /*
- X * Restore the original colour palette
- X */
- X for (i=0; i<16; i++) (void) Setcolor(i, atari_old_colour[i]);
- }
- X
- #endif
- X
- /*
- ** Generates a string with ansi colors
- ** The unix version checks the environment variable TERM
- */
- #ifdef __STDC__
- char *scr(int attribute,int foreground, int background)
- #else
- char *scr(attribute,foreground, background)
- int attribute,foreground,background;
- #endif
- {
- X char att_str[20],for_str[20],back_str[20];
- X int n = Color_no;
- X
- X if (!use_ansi)
- X {
- X sprintf (Color_string[0],""); /* NULL string */
- X return(Color_string[0]);
- X }
- X
- #ifdef ATARI
- X
- X Color_string[n][0] = '\0';
- X
- X if (atari_res==2 || atari_res==5)
- X return(Color_string[n]); /* Skip colour change on mono screens */
- X
- X if (foreground!=NONE) {
- X sprintf(for_str, "%cb%c", '\033', 'a'+foreground);
- X strcat(Color_string[n], for_str);
- X };
- X
- X if (background!=NONE) {
- X sprintf(back_str, "%cc%c", '\033', 'a'+background);
- X strcat(Color_string[n], back_str);
- X };
- X
- X
- #else
- X
- #ifndef __MSDOS__
- X if ( strstr((char *) getenv("TERM"),"ansi") == NULL)
- X {
- X Color_string[0][0] = '\0';
- X return (Color_string[0]);
- X }
- #endif
- X att_str[0] = '\0';
- X for_str[0] = '\0';
- X back_str[0] = '\0';
- X if (attribute != NONE)
- X {
- X sprintf (att_str,"%d",attribute);
- X if (foreground != NONE || background != NONE)
- X strcat(att_str,";");
- X }
- X if (foreground != NONE)
- X {
- X sprintf (for_str,"%d",30+foreground);
- X if (background != NONE)
- X strcat(for_str,";");
- X }
- X if (background != NONE)
- X sprintf (back_str,"%d",40+background);
- X sprintf (Color_string[n],"%s%s%s%sm",COL_HEAD,att_str,
- X for_str,back_str);
- #endif
- X Color_no++;
- X Color_no = Color_no%MAX_CLR;
- X return (Color_string[n]);
- }
- X
- void cls(void)
- {
- #ifndef __MSDOS__
- #ifdef ATARI
- X printf(CLEAR_HOME); fflush(stdout);
- #else
- X if ( strstr((char *) getenv("TERM"),"ansi") != NULL)
- X printf("\033[0;37;40m\033[2J\033H\n");
- X else
- X system("tput clear 2>/dev/null"); /* else use clear with terminfo */
- #endif
- #else
- X printf("\033[0;37;40m\033[2J\033[H");
- X fflush(stdout);
- #endif
- }
- X
- /* Remove unwanted characters */
- char *stripchars(line)
- char *line;
- {
- X char newstr[255];
- X char *str = line;
- X int i = 0;
- X
- X if (strlen(line) == 0)
- X return (NULL);
- X
- X while (*str)
- X {
- X if ((*str >= ' ') && (*str <= '~'))
- X newstr[i++]=*str;
- X str++;
- X }
- X newstr[i] = '\0';
- X strcpy (line,newstr);
- X
- X return(line);
- }
- X
- char *remove_cr(line)
- char *line;
- {
- X int l;
- X l = strlen(line);
- X if( line[l-1] == '\n')
- X line[l-1] = '\0';
- X return(line);
- }
- X
- /*
- ** Like fgets but with a prompt and check line length
- */
- void fgetstr( prompt, line, default_value, size, fr)
- char *prompt,*line,*default_value;
- int size;
- FILE *fr;
- {
- X int l=1,i;
- X if (fr == stdin)
- X {
- X /* We only display the prompt if we are reading from stdin */
- X printf ("%s%s%s ",scr(1,3,0),prompt,scr(1,1,0));
- X if (default_value && *default_value)
- X {
- X if ((int)strlen(default_value) > size)
- X default_value[size]='\0';
- X printf("%s",default_value);
- X l=strlen(default_value);
- X }
- X for (i = 0;i < size-l; i++)
- X printf (".");
- X printf ("\r%s%s%s ",scr(1,3,0),prompt,scr(8,7,0));
- X fflush(stdout);
- X }
- X fflush(fr);
- X if (fgets(line,size,fr) == NULL)
- X {
- X /* Return the default */
- X strcpy(line,default_value);
- X return;
- X }
- X line[size] = '\0';
- X l = strlen(line);
- X if (line[l-1] == '\n')
- X line[l-1] = '\0';
- X fflush (fr);
- X stripchars(line);
- X if (*line==0) /* Blank line */
- X strcpy (line,default_value);
- }
- X
- /*
- X This is the core of the program, where we build the index table
- X For each newsgroup we fill an entry in the group[] array
- X as well as for each article of a group.
- X This make moving between groups very quick.
- X However, reading an article can be slow : there is no index to point
- X directly to an article, we only know the rank of the article.
- X
- X However compared to the .ndx method used by QWK readers, this is not
- X dramatically slower. First of all the .dat files for QWK are bigger than
- X our MSG files : each article has to be adjusted to be on an exact
- X number of 128bytes block. And QWK .dat files contains header information
- X that are far too restrictive.
- */
- X
- void extract_article(ntext,size,separator,rewind_type)
- FILE *ntext;
- unsigned long size; /* Size of the article, if provided ignore the separator */
-
- char *separator;
- int rewind_type; /* if set to REWIND, will unget the last line */
- {
- X unsigned int l,end_of_article, headermode;
- X unsigned long bytes_read, line_cnt, file_pos;
- X char *pos,*p,temp[255],line[255];
- X
- X if (size==0)
- X l=strlen(separator);
- X headermode=1;
- X end_of_article=0;
- X line_cnt=1;
- X bytes_read=0;
- X
- X while (!end_of_article)
- X {
- X file_pos = ftell(ntext);
- X pos = fgets(line,255,ntext);
- X
- X if (pos==NULL)
- X end_of_article=1;
- X else
- X {
- X /* Have we reached the end ? */
- X bytes_read += strlen(line);
- X if (size) /* For usenet message */
- X {
- X if (bytes_read>=size)
- X end_of_article=1;
- X }
- X else if (strncmp(line,separator,l)==0)
- X end_of_article = 1;
- X
- X if (!end_of_article)
- X {
- X if (headermode)
- X {
- X if (line[0]=='\n')
- X {
- X headermode=0; /* End of Header, start of article body */
- X line_cnt=0;
- X }
- X line[strlen(line)-1]='\0';
- X if (strncmp(line,"From: ",6)==0)
- X {
- X /* Is there a name between () ? */
- X p=strchr(line,'(');
- X if (p)
- X {
- X strcpy (temp,p+1);
- X p=strrchr(temp,')');
- X
- X /* We need at least 4 chars in the name */
- X if (p < temp+4)
- X strcpy( temp, line+6);
- X else
- X *p='\0';
- X }
- X else /* Nope, just use the address as name */
- X strcpy(temp,line+6);
- X strncpy (article[cur_art].from,temp,16);
- X article[cur_art].from[16]='\0';
- X }
- X else if( strncmp(line,"Subject: ",9)==0 )
- X {
- X char *lpos = line+9;
- X while (( *lpos == ' ' ) || ( *lpos == '\t' ))
- X lpos++;
- X strncpy( article[cur_art].subject, lpos, 59);
- X article[cur_art].subject[50] = '\0';
- X }
- X }
- X else
- X line_cnt++;
- X }
- X }
- X }
- X article[cur_art].size=line_cnt;
- X if (rewind_type == REWIND)
- X fseek(ntext,file_pos,0);
- }
- X
- void index_all()
- {
- X char gline[255],line[255],*p,area_name[80];
- X int line_no=0,n;
- X unsigned long size, file_pos;
- X FILE *nlist,*ntext;
- X
- X /* Open the files */
- X if ((nlist=fopen(NEWSLIST,"r"))==NULL)
- X {
- X fprintf (stderr,"Unable to open <%s>\n",NEWSLIST);
- X exit(1);
- X }
- X
- X /* For each newsgroup */
- X while (fgets(gline,255,nlist)!=NULL)
- X {
- X line_no++;
- X if (gline[0]!='#') /* Ignore commented out lines */
- X {
- X /* Ignore comments at end of line */
- X if ((p=strrchr(gline,'#'))!=NULL)
- X *p='\0';
- X if (gline[strlen(gline)-1]=='\n')
- X gline[strlen(gline)-1]='\0';
- X
- X /*
- X ** Get the prefix name and open the file
- X */
- X if ((p=strchr(gline,'\t'))==NULL)
- X {
- X fprintf (stderr,"Invalid entry in the AREAS file, line %d\n",line_no);
- X exit(1);
- X }
- X *p='\0';
- X sprintf(area_name,"%s%s",gline,NEWSTXT);
- X if ((ntext=fopen(area_name,"r"))==NULL)
- X {
- X fprintf(stderr,"Unable to open area <%s>\n",area_name);
- X exit(1);
- X }
- X strcpy(group[cur_grp].prefix,gline);
- X group[cur_grp].start=cur_art;
- X strcpy(gline,p+1);
- X
- X /*
- X ** Get the group name
- X */
- X if ((p=strchr(gline,'\t'))==NULL)
- X {
- X fprintf (stderr,"Invalid entry in the AREAS file, line %d\n",line_no);
- X exit(1);
- X }
- X *p='\0';
- X strcpy (group[cur_grp].name,gline);
- X p++;
- X strcpy (gline,p);
- X p=gline;
- X
- X /*
- X ** Get the types
- X */
- X if (*p=='\0')
- X {
- X fprintf (stderr,"Invalid entry in the AREAS file, line %d\n",line_no);
- X exit(1);
- X }
- X group[cur_grp].msg_type=*p;
- X p++;
- X if (*p=='\0')
- X {
- X fprintf (stderr,"Invalid entry in the AREAS file, line %d\n",line_no);
- X exit(1);
- X }
- X group[cur_grp].idx_type=*p;
- X p++;
- X if (*p=='\0') /* No optional field */
- X goto area_done;
- X strcpy(gline,p+1);
- X
- X /*
- X ** Get the group description
- X */
- X if ((p=strchr(gline,'\t'))==NULL)
- X {
- X strcpy(group[cur_grp].desc,gline);
- X goto area_done;
- X }
- X *p='\0';
- X strcpy(group[cur_grp].desc,gline);
- X strcpy(gline,p+1);
- X
- #ifdef NEVER /* This may be needed in the future */
- X /*
- X ** Get the number of articles
- X */
- X if ((p=strchr(gline,'\t'))==NULL)
- X {
- X group[cur_grp].art = atol(gline);
- X goto area_done;
- X }
- X *p='\0';
- X group[cur_grp].art = atol(gline);
- X strcpy (gline,p+1);
- #endif
- X
- area_done:
- X printf("\nReading %s ", group[cur_grp].name );
- X
- X /*
- X ** For each article
- X */
- X n=0;
- X file_pos = ftell(ntext);
- X while(fgets(line,255,ntext)!=NULL)
- X {
- X if ((n%10)==0)
- X {
- X printf (".");
- X fflush(stdout);
- X }
- X switch(group[cur_grp].msg_type)
- X {
- X case 'm': /* Mailbox format */
- X /*
- X ** Read the From line
- X */
- X if (strncmp(line,MAIL_SEPARATOR,5))
- X {
- X fprintf(stderr,"Line <%s> not in mail format\n",line);
- X exit(1);
- X }
- X article[cur_art].ptr = file_pos;
- X extract_article(ntext,0L,MAIL_SEPARATOR,REWIND);
- X break;
- X
- X case 'M': /* MMDF format */
- X /*
- X ** Read the Ctrl As line
- X */
- X if (strncmp(line,MMDF_SEPARATOR,4))
- X {
- X fprintf(stderr,"Line <%s> not in MMDF format\n",line);
- X exit(1);
- X }
- X article[cur_art].ptr = ftell(ntext);
- X extract_article(ntext,0L,MMDF_SEPARATOR,0);
- X break;
- X
- X case 'u': /* Usenet news format */
- X /*
- X ** Read the rnews line
- X */
- X if (strncmp(line,"#! rnews ",9))
- X {
- X fprintf(stderr,"Line <%s> not in rnews format\n",line);
- X exit(1);
- X }
- X size = atol(line+9);
- X article[cur_art].ptr = ftell(ntext);
- X if( size )
- X extract_article(ntext,size,"",0);
- X break;
- X
- X default:
- X fprintf(stderr,"Error: Message type <%c> not supported\n",
- X group[cur_grp].msg_type);
- X exit(1);
- X }
- X article[cur_art].rank=n;
- X /*
- X ** Here should be the code to malloc() a new article
- X ** For the moment we just check that we are not going
- X ** further than the boundary
- X */
- X if (cur_art>=MAXART)
- X return;
- X cur_art++;
- X n++;
- X file_pos = ftell(ntext);
- X }
- X group[cur_grp].art=n;
- X /*
- X ** Here should be the code to malloc() a new group
- X ** For the moment we just check that we are not going
- X ** further than the boundary
- X */
- X fclose(ntext);
- X if (cur_grp>=MAXGRP)
- X return;
- X cur_grp++;
- X }
- X }
- X fclose (nlist);
- }
- X
- void disp_f(ptr)
- struct arts *ptr;
- {
- X printf ("%s%3ld %s%-16s %s%4ld %s%-53s\n",
- X (ptr->rank+1)==no ? scr(1,GREEN,0) : scr(1,YELLOW,0), ptr->rank+1,
- X scr(1,MAGENTA,0),ptr->from,
- X scr(1,YELLOW,0),ptr->size+1,
- X scr(1,CYAN,0),ptr->subject);
- }
- X
- void show_header()
- {
- X int n=group[cur_grp].art;
- X char desc[80];
- X
- X cls();
- X sprintf( desc, "%s <%s>", group[cur_grp].name, group[cur_grp].desc );
- X desc[59] = '\0';
- X
- X printf( "%sGroup %s%-60s %s%d %sarticle%c\n",
- X scr( 1, GREEN, 0 ), scr( 1, CYAN, 0 ), desc,
- X scr(1,YELLOW,0),n,scr(1,GREEN,0),(n>1) ? 's' : ' ');
- X
- X printf (" %sFrom Lines Subject\n",scr(1,CYAN,0));
- X printf ("%s==================================================================
- ==========\n",scr(1,GREEN,0));
- }
- X
- char ch_get(void)
- {
- #ifdef ATARI
- X return((char)Cnecin());
- #else
- #ifdef __MSDOS__
- X return (getch());
- #else
- #ifdef OS2
- X return (getch());
- #else
- X int file;
- X char return_char;
- X struct termio newterm, oldterm;
- X file = open("/dev/tty", O_RDONLY);
- X if (file == -1)
- X return (0);
- X ioctl(file,TCGETA,&oldterm);
- X memcpy (&newterm,&oldterm,sizeof(struct termio));
- X newterm.c_cc[4] = 1;
- X newterm.c_cc[5] = 0;
- X /* newterm.c_cflag = (B9600|CS8|CREAD); */
- X newterm.c_lflag = 0;
- X newterm.c_iflag = IGNBRK;
- X newterm.c_oflag = 0;
- X ioctl (file,TCSETAF,&newterm);
- X read(file,&return_char,1);
- X ioctl (file,TCSETAF,&oldterm);
- X close(file);
- X return (return_char);
- #endif
- #endif
- #endif
- }
- X
- X
- #ifdef __STDC__
- int edit_file(char *name,char *header)
- #else
- int edit_file(name,header)
- char *name, *header;
- #endif
- {
- X char temp[80],temp2[80],choice;
- X int end=0;
- X FILE *fr;
- X
- X sprintf (temp,"%s %s",edit_str,name);
- X sprintf (temp2,"%s %s",edit_str,header);
- X system(temp);
- X while (!end)
- X {
- X printf("%sP%sost, %sE%sdit_file %sH%seader_edit or %sA%sbandon : ",
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0),scr(1,3,0),scr(1,6,0));
- X fflush(stdout);
- X
- X choice=ch_get();
- X switch(choice)
- X {
- X case 'p':
- X case 'P':
- X /* Does the article exist ? */
- X if ((fr=fopen(name,"r"))==NULL)
- X return(1);
- X fclose(fr);
- X end = 1;
- X break;
- X
- X case 'E':
- X case 'e':
- X system(temp);
- X break;
- X
- X case 'H':
- X case 'h':
- X system(temp2);
- X break;
- X
- X case 'a':
- X case 'A':
- X remove(name);
- X end = 2;
- X }
- X }
- X return( end-1 );
- }
- X
- #ifdef __STDC__
- unsigned long add_file (char *f1,char *f2)
- #else
- unsigned long add_file (f1,f2)
- char *f1,*f2;
- #endif
- {
- X char header[80];
- X FILE *df1,*df2;
- X int c;
- X unsigned long length;
- X struct stat buf;
- X
- X if ( ((df1=fopen(f1,"a"))==NULL) || ((df2=fopen(f2,"r"))==NULL) )
- X {
- X fprintf (stderr,"Unable to open a file [%s] or [%s]!\n",f1,f2);
- X return (0);
- X }
- X
- X stat(f2,&buf);
- X length=(unsigned long)buf.st_size;
- X sprintf (header,"#! rnews %ld\n",length);
- X length += strlen(header);
- X fprintf (df1,"%s",header);
- X
- X while ((c=fgetc(df2))!=EOF)
- X fputc(c,df1);
- X
- X fclose(df1);
- X fclose(df2);
- X return(length);
- }
- X
- X
- /*
- ** Append the content of the signature file to the file name
- */
- #ifdef __STDC__
- void addsig(char *name)
- #else
- void addsig(name)
- char *name;
- #endif
- {
- X FILE *fr,*fw;
- X char line[81];
- X
- X if ((fr=fopen("sig.txt","r"))==NULL)
- X return;
- X if ((fw=fopen(name,"a"))==NULL)
- X return;
- X fprintf(fw,"\n-- \n");
- X while (fgets(line,80,fr)!=NULL)
- X fprintf(fw,"%s",line);
- X fclose(fr);
- X fclose(fw);
- }
- X
- char *upperit(line)
- char *line;
- {
- X char *pos=line;
- X while (*pos)
- X {
- X *pos=toupper(*pos);
- X pos++;
- X }
- X return (line);
- }
- X
- int get_set_unset(line)
- char *line;
- {
- X if( ( line[0]=='Y') || (line[0]=='y') ||
- X (line[0]=='S') || (line[0]=='s'))
- X return(SET);
- X
- X if( ( line[0] == 'N' ) || ( line[0] == 'n' ) ||
- X (line[0]=='U') || (line[0]=='u'))
- X return(UNSET);
- X
- X fprintf( stderr, "Error in config file: Line <%s>"
- X "should be either SET or UNSET\n",line);
- X return( -1 );
- }
- X
- int get_set_unset_ask(line)
- char *line;
- {
- X if( ( line[0] == 'Y' ) || ( line[0] == 'y' ) ||
- X ( line[0] == 'S' ) || ( line[0] == 's' ) )
- X return(SET);
- X
- X if( ( line[0] == 'N' ) || ( line[0] == 'n' ) ||
- X ( line[0] == 'U' ) || ( line[0] == 'u' ) )
- X return(UNSET);
- X
- X if((line[0]=='A') || (line[0]=='a'))
- X return(ASK);
- X fprintf(stderr,"Error in config file: Line <%s>"
- X " should be either SET, UNSET or ASK\n",line);
- X return( -1 );
- }
- X
- /*
- ** Get a token from the token list out of the line
- ** update pos as the position in the line after the token
- ** and return the token number if found, -1 else.
- */
- #ifdef __STDC__
- int get_token(char *line,char **tlist,char **pos)
- #else
- int get_token(line,tlist,pos)
- char *line, **tlist,**pos;
- #endif
- {
- X int i=0,l,ll;
- X char *c;
- X
- X /* search for the "=" separator */
- X c=strchr(line,'=');
- X if (c==NULL)
- X return (-1);
- X ll = (int)(c-line); /* Get line length */
- X *c='\0';
- X *pos=c+1; /* *pos points to the first character after the = */
- X upperit(line);
- X while (tlist[i][0])
- X {
- X l=strlen(tlist[i]);
- X if((l==ll) && (strcmp(line,tlist[i])==0))
- X return(i);
- X i++;
- X }
- X return(-1); /* failure */
- }
- X
- /*
- ** Open the slnr.ini file and read the config for
- ** editor, ansi, pager, compress
- */
- void read_config()
- {
- X FILE *fr;
- X int n;
- X char line[80],old_line[80],*pos;
- X static char *tlist[7]={"EDITOR",
- X "PAGER",
- X "COMPRESS",
- X "ANSI",
- X "LINES",
- X "QUOTE",
- X ""};
- X
- X /*
- X ** Default
- X */
- X
- X strcpy (edit_str,EDIT);
- X strcpy (pager_str,PAGER);
- X strcpy (compress_str,COMPRESS);
- X strcpy (quote_str,"> ");
- X use_ansi = SET;
- X Lines = 24;
- X
- X if ((fr=fopen(INITFILE,"r"))==NULL)
- X return; /* No config file */
- X while (fgets(line,80,fr)!=NULL)
- X {
- X strcpy (old_line,line);
- X if (line[0]!='#' && line[0]!='\n') /* Ignore comments and blank lines */
- X {
- X if (line[strlen(line)-1]=='\n') /* Remove end CR */
- X line[strlen(line)-1]='\0';
- X switch (get_token(line,tlist,&pos))
- X {
- X case 0: /* EDITOR */
- X strcpy (edit_str,pos);
- X break;
- X
- X case 1: /* PAGER */
- X strcpy (pager_str,pos);
- X break;
- X
- X case 2: /* COMPRESS */
- X strcpy (compress_str,pos);
- X break;
- X
- X case 3: /* Ansi */
- X if( (n = get_set_unset(pos)) != -1 )
- X use_ansi = n;
- X break;
- X
- X case 4: /* Lines */
- X Lines = atoi(pos);
- X if (Lines<5)
- X Lines = 24;
- X break;
- X
- X case 5: /* Quote string */
- X /* Skip the spaces */
- X while ((*pos==' ') || (*pos=='\t'))
- X pos++;
- X
- X if( *pos == '"')
- X {
- X strcpy(quote_str,pos+1);
- X /* Find the matching double quote */
- X pos = strrchr(quote_str,'"');
- X if (pos)
- X *pos = '\0';
- X }
- X else
- X strcpy(quote_str,pos);
- X remove_cr(quote_str);
- X break;
- X
- X default:
- X fprintf(stderr,"Line <%s> has no keyword\n",old_line);
- X break;
- X }
- X }
- X }
- X fclose(fr);
- }
- X
- /*
- ** Put the message pointed by fr into the filename and
- ** returns the number of lines in the header
- ** or 0 if an error occured
- ** And quote the message with the quote string
- */
- X
- int put_into_file(filename,fr,quote,flags,head)
- char *filename;
- FILE *fr;
- char *quote;
- struct header *head;
- unsigned long flags;
- {
- X FILE *fw;
- X int end=0,l,headermode=1,line_no=1,mail_flag=0;
- X char separator[80],line[255],mode[5];
- X
- X strcpy( mode, "w" ); /* default */
- X
- X if(( fw = fopen( filename, "r" ) ) != NULL )
- X {
- X fclose( fw );
- X printf( "File <%s> already exists\n", filename );
- X while( !end )
- X {
- X printf( "[A]ppend [O]verwrite [Q]uit:" );
- X fflush( stdout );
- X switch( ch_get() )
- X {
- X case 'q':
- X case 'Q':
- X return(0);
- X
- X case 'a':
- X case 'A':
- X strcpy( mode, "a" );
- X end=1;
- X break;
- X
- X case 'O':
- X case 'o':
- X strcpy( mode, "w" );
- X end=1;
- X break;
- X }
- X }
- X }
- X
- X if((fw=fopen(filename, mode))==NULL)
- X return(0);
- X
- X if(group[cur_grp].msg_type=='M')
- X strcpy(separator, MMDF_SEPARATOR);
- X else if(group[cur_grp].msg_type=='m')
- X {
- X strcpy(separator, "From ");
- X mail_flag = 1;
- X }
- X else
- X strcpy(separator,"#! rnews");
- X l = strlen(separator);
- X
- X fgets(line,255,fr);
- X if (mail_flag)
- X {
- X if (!(flags & NO_HEADER))
- X fprintf (fw,"%s",line);
- X line_no++;
- X if( fgets( line, 255, fr ) == NULL )
- X return(0);
- X }
- X
- X while( strncmp( line, separator, l ))
- X {
- X if( headermode )
- X {
- X if (!(flags & NO_HEADER))
- X fprintf (fw,"%s",line);
- X
- X if( (flags & GET_SUBJECT) && strncmp(line,"Subject: ",9)==0)
- X strcpy( head->subject, line+9);
- X
- X if( (flags & GET_ID) && strncmp(line,"Message-ID: ",12)==0)
- X strcpy( head->id, line+12);
- X
- X if( (flags & GET_FROM) && strncmp(line,"From: ",6)==0)
- X strcpy( head->from, line+6);
- X
- X if( (flags & GET_REPLYTO) && strncmp(line,"Reply-To: ",10)==0)
- X strcpy( head->replyto, line+10);
- X }
- X else
- X {
- X fprintf( fw,"%s%s",quote,line);
- X line_no++;
- X }
- X
- X if (line[0]=='\n') /* End of header */
- X headermode=0;
- X
- X if(fgets(line,255,fr)==NULL)
- X break;
- X }
- X fclose(fw);
- X return(line_no);
- }
- X
- void show_menu(tof,eof)
- unsigned int tof,eof;
- {
- X printf ("\n%sA%sgain %sV%siew %sM%sail ",
- X scr(1,3,0),scr(1,6,0),scr(1,3,0),scr(1,6,0),scr(1,3,0),scr(1,6,0));
- X if (group[cur_grp].msg_type=='u')
- X /* No follow-up for private mail */
- X printf ("%sW%srite ",scr(1,3,0),scr(1,6,0));
- X if (!eof)
- X printf ("%sn%sext_pg ",
- X scr(1,3,0),scr(1,6,0));
- X if (!tof)
- X printf ("%sp%srev_pg ",
- X scr(1,3,0),scr(1,6,0));
- X printf ("%sN%sext_grp %sP%srev_grp %sS%save %sH%slp %sQ%suit ",
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0));
- X printf("%s", scr(1, WHITE, 0));
- X fflush(stdout);
- }
- X
- void display_page_again(line_no)
- unsigned int line_no;
- {
- X int i,j;
- X
- X show_header();
- X cur_art=old_cur_art;
- X for (j=0;j<cur_line;j++)
- X {
- X disp_f(&article[cur_art]);
- X cur_art++;
- X }
- X if (cur_line!=line_no)
- X {
- X for (i=cur_line; i<=line_no; i++)
- X printf("\n");
- X }
- }
- X
- /*
- ** Get a clean address to be passed back to the mailer
- */
- char *clean_to( to )
- char *to;
- {
- X char *pos1, *pos2;
- X
- X /*
- X ** Remove the comment between ()
- X */
- X if( (pos1 = strchr( to, '(' )) != NULL )
- X {
- X if( (pos2=strrchr(pos1,')')) != NULL )
- X strcpy( pos1, pos2+1 );
- X else
- X *pos1 = '\0';
- X }
- X /*
- X ** Get the address between <>
- X */
- X if( (pos1 = strchr( to, '<' )) != NULL )
- X {
- X strcpy( to, pos1+1 );
- X if( (pos2 = strchr( to, '>' )) != NULL )
- X *pos2 = '\0';
- X }
- X return( to );
- }
- X
- int main()
- {
- X unsigned int count, line_no,i=0,j=0,end=0,eof=0;
- X unsigned int reply_created=0,ret;
- X unsigned int tof=1,redisplay=1,Max_grp,Max_art,mail_reply_no=0;
- X char line[255],temp[80],choice,value[80],file_list[255];
- X char To[80],Replyto[80],From[80];
- X char Reply[80],subjectline[80],msgidline[80];
- X char art_save_name[80],shell_cmd_name[80];
- X FILE *fr,*fw;
- X struct header head;
- X
- #ifdef ATARI
- X initialise_atari();
- #endif
- X
- #ifdef __MSDOS__ /* Text must be translated so if user edits INI */
- X _fmode = O_TEXT ; /* file with an MS-DOS editor, command lines */
- #endif /* won't be truncated with \r */
- X
- X read_config();
- X
- #ifdef __MSDOS__ /* Unix-type files must not be translated so */
- X _fmode = O_BINARY ; /* that pointers inside them are valid. */
- #endif
- X
- X To[0]=Replyto[0]=From[0]=art_save_name[0]=shell_cmd_name[0]='\0';
- X cls();
- X printf ("\n%sS%simple %sL%socal %sN%sews %sR%seader. V 2.0a (c) 1993 Philippe
- Goujard\n",
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0),
- X scr(1,3,0),scr(1,6,0));
- X /*
- X ** To be changed when we have another compressor
- X */
- X sprintf (Reply,"REPLY.zip");
- X if ((fr=fopen(Reply,"r"))!=NULL)
- X {
- X fclose (fr);
- X printf ("\nA Reply packet already exists. Keep it (Y/n) ? ");
- X fflush(stdout);
- X choice=ch_get();
- X if (choice=='n' || choice=='N')
- X remove (Reply);
- X fflush(stdin);
- X }
- X for (i=0; i<MAXGRP; group[i++].reply=0)
- X ;
- X
- X index_all();
- X
- X Max_grp=cur_grp-1;
- X Max_art=cur_art; /* Not needed in this version */
- X
- X cur_art=0;
- X cur_grp=0;
- X
- #ifdef INFOCOM
- X /*
- X ** This is only done with Infocom where a user can define a profile which
- X ** is independant of the Unix environment. It can be easily changed to a
- X ** "GETENV"
- X */
- X if (get_prof("LINES",value)>0)
- X {
- X line_no = atoi(value)-6;
- X if (line_no < 7)
- X line_no=18;
- X }
- #else
- X line_no = Lines-6;
- #endif
- X /* Displays header */
- X show_header();
- X
- X /* Displays list upto LINES - 6 */
- X i=0;
- X
- X while ((cur_line<line_no) && cur_line<group[cur_grp].art)
- X {
- X disp_f(&article[cur_art]);
- X cur_line++;
- X cur_art++;
- X }
- X if (cur_line!=line_no)
- X {
- X eof=1;
- X for (i=cur_line; i<=line_no; i++)
- X printf("\n");
- X }
- X
- X /*
- X ** MAIN LOOP
- X */
- X while (!end)
- X {
- X /* Displays footer */
- X if (redisplay)
- X show_menu( tof, eof);
- X
- X choice=ch_get();
- X redisplay=1;
- X switch (choice)
- X {
- X case 'A':
- X case 'a':
- X display_page_again(line_no);
- X break;
- X
- X case 'n': /* Next page */
- X if (eof)
- X redisplay=0;
- X else
- X {
- X tof=0;
- X show_header();
- X cur_line=1;
- X old_cur_art=cur_art;
- X while ((cur_line<line_no) &&
- X (article[cur_art].rank+1 < group[cur_grp].art))
- X {
- X disp_f(&article[cur_art]);
- X cur_line++;
- X cur_art++;
- X }
- X disp_f(&article[cur_art++]);
- X if (cur_line!=line_no)
- X {
- X eof=1;
- X for (i=cur_line; i<=line_no; i++)
- X printf("\n");
- X }
- X else
- X eof=0;
- X }
- X break;
- X
- X case 'p': /* Previous page */
- X if (tof)
- X redisplay=0;
- X else
- X {
- X eof=0;
- X show_header();
- X cur_art = old_cur_art - line_no;
- X old_cur_art = cur_art;
- X if (article[cur_art].rank==0)
- X tof=1;
- X else
- X tof=0;
- X for (cur_line=0; cur_line<line_no; cur_line++)
- X {
- X disp_f(&article[cur_art]);
- X cur_art++;
- X }
- X }
- X break;
- X
- X case 'N': /* Next group */
- X case '>':
- X no = 0;
- X cur_grp++;
- X if (cur_grp>Max_grp)
- X cur_grp=0;
- X cur_line=0;
- X eof=0;
- X tof=1;
- X show_header();
- X cur_art=group[cur_grp].start;
- X old_cur_art=cur_art;
- X while ((cur_line<line_no) && cur_line<group[cur_grp].art)
- X {
- X disp_f(&article[cur_art]);
- X cur_line++;
- X cur_art++;
- X }
- X if (cur_line!=line_no)
- X {
- X eof=1;
- X for (i=cur_line; i<=line_no; i++)
- X printf("\n");
- X }
- X break;
- X
- X case 'P': /* Prev group */
- X case '<':
- X no = 0;
- X if (cur_grp == 0)
- X cur_grp=Max_grp;
- X else
- X cur_grp--;
- X cur_line=0;
- X eof=0;
- X tof=1;
- X cur_art=group[cur_grp].start;
- X old_cur_art=cur_art;
- X show_header();
- X while ((cur_line<line_no) && cur_line<group[cur_grp].art)
- X {
- X disp_f(&article[cur_art]);
- X cur_line++;
- X cur_art++;
- X }
- X if (cur_line!=line_no)
- X {
- X eof=1;
- X for (i=cur_line; i<=line_no; i++)
- X printf("\n");
- X }
- X break;
- X
- X case 'H':
- X case 'h':
- X cls();
- X /*
- X ** Help doesnt help much at the moment
- X ** Well, c'est la vie...
- X */
- X printf ("A : redisplay the same page\n"
- X "n : Display next page\n"
- X "p : Display previous page \n"
- X "N or > : Go to next Newsgroup\n"
- X "P or < : Go to previous Newsgroup\n"
- X "v : View an article\n"
- X "V : Display an article with its full header\n"
- X "M : Send a new mail or Reply to an article by mail\n"
- X "W : Write an article or a follow-up to an article\n"
- X "SPACE : Display the next article\n"
- X "s : Save an article to a filename\n"
- X "S : Save an article without the header\n"
- X "! : Execute a shell command\n"
- X "Q : Quit \n\n");
- X break;
- X
- X case 'Q':
- X case 'q':
- X printf ("\n"); /* To have the prompt on a new line */
- X end=1;
- X file_list[0]='\0';
- X /*
- X ** Compress the R*.MSG files
- X */
- X if (reply_created)
- X {
- X /*
- X ** For each newsgroup where there is a reply
- X ** Add an entry to the REPLIES file
- X ** The field "reply" is set only for "follow-up" or "posts"
- X ** Not for mails or replies by mail to the author.
- X */
- X if ((fw=fopen("REPLIES","w"))==NULL)
- X exit(1);
- X for (i=0;i<MAXGRP;i++)
- X {
- X if(group[i].reply)
- X {
- X fprintf(fw,"R%s\tnews\t%cn\t%d\n",
- X group[i].prefix,
- X group[i].msg_type,
- X group[i].reply);
- X /*
- X ** Add the filename to the list of files
- X ** to compress
- X */
- X sprintf(temp,"R%s%s ",group[i].prefix,NEWSTXT);
- X strcat(file_list,temp);
- X }
- X }
- X fclose(fw);
- X }
- X /*
- X ** Now look if any mail reply was created too
- X */
- X if ((fr=fopen("RMAIL.MSG","r"))!=NULL)
- X {
- X fclose(fr);
- X fw=fopen("REPLIES","a");
- X fprintf(fw,"RMAIL\tmail\tun\t%d\n", mail_reply_no);
- X sprintf(temp,"RMAIL%s ",NEWSTXT);
- X strcat(file_list,temp);
- X fclose(fw);
- X }
- X if (file_list[0])
- X {
- X sprintf (temp,"%s %s REPLIES %s",compress_str,Reply,file_list);
- X system (temp);
- X }
- X
- #ifdef ATARI
- X closedown_atari();
- #endif
- X break;
- X
- X case 'V':
- X case 'v':
- X printf ("\n");
- X sprintf (value,"%d",no+1);
- X fgetstr("No >",temp,value,4,stdin);
- X no = atoi(temp);
- showit:
- X if ((no>0) && (no <= group[cur_grp].art))
- X {
- X sprintf(temp,"%s%s",group[cur_grp].prefix,NEWSTXT);
- X fr=fopen(temp,"r");
- X /* Get the starting article number */
- X count=group[cur_grp].start+no-1;
- X
- X /* save the article into a temp file */
- X fseek(fr, article[count].ptr, 0);
- X put_into_file("art.tmp",fr,"",
- X (choice=='V') ? 0L : NO_HEADER,
- X (struct header *)NULL);
- X fclose(fr);
- X
- X sprintf (temp,"%s %s",pager_str,"art.tmp");
- X system(temp);
- X remove("art.tmp");
- X /*
- X ** Show the list again
- X */
- X show_header();
- X cur_art=old_cur_art;
- X for (j=0;j<cur_line;j++)
- X {
- X disp_f(&article[cur_art]);
- X cur_art++;
- X }
- X if (cur_line!=line_no)
- X {
- X eof=1;
- X for (i=cur_line; i<=line_no; i++)
- X printf("\n");
- X }
- X }
- X break;
- X
- X case ' ':
- X if(no < group[cur_grp].art)
- X no++;
- X goto showit;
- X
- X case 'M': /* Mail somebody */
- X case 'm':
- X printf ("\n%sN%sew message or %sR%sesponse ?",
- X scr(1,3,0),scr(1,6,0),scr(1,3,0),scr(1,6,0));
- X fflush(stdout);
- X choice=ch_get();
- X printf ("\n");
- X subjectline[0]='\0';
- X if (choice=='r' || choice=='R')
- X {
- X /* Ask to which mail */
- X sprintf (value,"%d",no);
- X fgetstr("Reply to No >",temp,value,4,stdin);
- X no=atoi(temp);
- X if ((no>0) && (no <= group[cur_grp].art) )
- X {
- X /*
- X ** Get article and quote it
- X */
- X sprintf(temp,"%s%s",group[cur_grp].prefix,NEWSTXT);
- X fr=fopen(temp,"r");
- X count=group[cur_grp].start+no-1;
- X fseek(fr, article[count].ptr, 0);
- X
- X /* save the article into a temp file */
- X head.subject = line;
- X head.from = From;
- X head.replyto = Replyto;
- X
- X ret = put_into_file( "ed0.tmp",fr, quote_str,
- X NO_HEADER | GET_SUBJECT | GET_FROM | GET_REPLYTO, &head);
- X fclose(fr);
- X
- X if( ret == 0 )
- X {
- X perror( "Unable to write temp file" );
- X break;
- X }
- X
- X /* Does the subject line begin with "Re" ? */
- X remove_cr( line);
- X if (strncmp(line,"Re",2)==0)
- X sprintf(subjectline,"%s",line);
- X else
- X sprintf(subjectline,"Re: %s",line);
- X
- X if (*Replyto)
- X strcpy( To, Replyto);
- X else if (*From)
- X strcpy( To, From);
- X remove_cr( To );
- X clean_to( To );
- X
- X /*
- X ** Add the group in the body of the quoted article
- X */
- X remove_cr(From);
- X fr = fopen( "ed0.tmp","r" );
- X fw = fopen( "edit.tmp","w" );
- X fprintf( fw, "In %s you wrote:\n", group[cur_grp].name);
- X while( fgets(line,255,fr)!=NULL )
- X {
- X fprintf( fw, "%s", line );
- X }
- X fclose( fr );
- X fclose( fw );
- X remove( "ed0.tmp" );
- X }
- X }
- X /* Print the header first */
- X fw=fopen ("article.tmp","w");
- X if (fw==NULL)
- X break;
- X
- X if (!*To)
- X fgetstr("To >",To,"",60,stdin);
- X fgetstr("Subject >",temp,subjectline,60,stdin);
- X fprintf (fw,"To: %s\n",To);
- X fprintf (fw,"Subject: %s\n",temp);
- X fclose(fw);
- X To[0]=From[0]=Replyto[0]='\0';
- X
- X if (edit_file("edit.tmp","article.tmp"))
- X remove("article.tmp");
- X else
- X {
- X /* Add the signature */
- X addsig("edit.tmp");
- X
- X /* Concatenate article.tmp and edit.tmp */
- X fr=fopen("edit.tmp","r");
- X fw=fopen("article.tmp","a");
- X fprintf (fw,"\n"); /* End of header */
- X while (fgets(line,255,fr)!=NULL)
- X fprintf(fw,"%s",line);
- X fclose(fr);
- X fclose(fw);
- X remove("edit.tmp");
- X
- X /*
- X ** Add article.tmp to the RMAIL.MSG file
- X */
- X sprintf (temp,"RMAIL%s",NEWSTXT);
- X add_file(temp,"article.tmp");
- X remove("article.tmp");
- X mail_reply_no++;
- X }
- X /* Then redisplay the list */
- X display_page_again(line_no);
- X break;
- X
- X case 'W': /* write article or follow-up */
- X case 'w':
- X if( group[cur_grp].msg_type != 'u')
- X break;
- X
- X printf ("\n%sN%sew article or %sF%sollow-up ?",
- X scr(1,3,0),scr(1,6,0),scr(1,3,0),scr(1,6,0));
- X choice=ch_get();
- X printf ("\n");
- X subjectline[0]='\0';
- X msgidline[0]='\0';
- X if (choice=='f' || choice=='F')
- X {
- X /*
- X ** Follow-up.
- X ** Ask to which article
- X */
- X sprintf (value,"%d",no);
- X fgetstr("Follow up to No >",temp,value,4,stdin);
- X no=atoi(temp);
- X if ((no>0) && (no <= group[cur_grp].art)
- X && (group[cur_grp].msg_type=='u'))
- X {
- X /*
- X ** Get article and quote it
- X */
- X sprintf(temp,"%s%s",group[cur_grp].prefix,NEWSTXT);
- X fr=fopen(temp,"r");
- X count=group[cur_grp].start+no-1;
- X fseek(fr, article[count].ptr, 0);
- X
- X /*
- X ** save the article into a temp file
- X */
- X head.subject = line;
- X head.id = msgidline;
- X head.from = From;
- X
- X ret = put_into_file( "ed0.tmp",fr, quote_str,
- X NO_HEADER | GET_SUBJECT | GET_ID | GET_FROM, &head);
- X fclose(fr);
- X if( ret == 0 )
- X {
- X perror( "Unable to write temp file" );
- X break;
- X }
- X
- X remove_cr(line);
- X if (strncmp(line,"Re",2)==0)
- X sprintf(subjectline,"%s",line);
- X else
- X sprintf(subjectline,"Re: %s",line);
- X
- X /*
- X ** Add the author name and group in the body
- X ** of the quoted article
- X */
- X remove_cr(From);
- X fr = fopen( "ed0.tmp","r" );
- X fw = fopen( "edit.tmp","w" );
- X fprintf( fw, "%s wrote:\n", From);
- X while( fgets(line,255,fr)!=NULL )
- X {
- X fprintf( fw, "%s", line );
- X }
- X fclose( fr );
- X fclose( fw );
- X remove( "ed0.tmp" );
- X
- X }
- X }
- X /* Print the header first */
- X fw=fopen ("article.tmp","w");
- X if (fw==NULL)
- X break;
- X
- X fflush(stdin);
- X fgetstr("Subject >",temp,subjectline,60,stdin);
- X fprintf (fw,"Newsgroups: %s\n",group[cur_grp].name);
- X fprintf (fw,"Subject: %s\n",temp);
- X
- X /* If its a follow-up, add reference to help threading */
- X if (msgidline[0])
- X fprintf (fw,"References: %s",msgidline);
- X
- X fclose(fw);
- X
- X if (edit_file("edit.tmp","article.tmp"))
- X remove("article.tmp");
- X else
- X {
- X /* Add the signature */
- X addsig("edit.tmp");
- X
- X /* Concatenate article.tmp and edit.tmp */
- X fr = fopen( "edit.tmp", "r");
- X fw = fopen( "article.tmp", "a");
- X fprintf( fw,"\n" ); /* End of header */
- X while( fgets(line,255,fr)!=NULL )
- X fprintf( fw,"%s",line );
- X fclose(fr);
- X fclose(fw);
- X remove("edit.tmp");
- X /*
- X ** Add article.tmp to the reply file
- X */
- X sprintf (temp,"R%s%s",group[cur_grp].prefix,NEWSTXT);
- X add_file(temp,"article.tmp");
- X remove("article.tmp");
- X reply_created++;
- X group[cur_grp].reply++;
- X }
- X /* Then redisplay the list */
- X display_page_again(line_no);
- X break;
- X
- X case 's': /* Save article */
- X case 'S': /* Save with no header */
- X printf("\n");
- X
- X /* Get article number */
- X sprintf (value,"%d",no);
- X fgetstr("Save article No >",temp,value,4,stdin);
- X no=atoi(temp);
- X
- X /* If the number is within range */
- X if ((no>0) && (no <= group[cur_grp].art))
- X {
- X
- X /* Get file name */
- X fgetstr("Save article in:",temp,art_save_name,30,stdin);
- X if ( temp[0] )
- X {
- X strcpy(art_save_name, temp);
- X
- X sprintf(temp,"%s%s",group[cur_grp].prefix,NEWSTXT);
- X fr=fopen(temp,"r");
- X count=group[cur_grp].start+no-1;
- X fseek(fr, article[count].ptr, 0);
- X
- X /*
- X ** save the article into a temp file
- X */
- X if (!put_into_file( art_save_name, fr, "",
- X (choice=='S') ? NO_HEADER : 0L,
- X (struct header *)NULL))
- X perror("Unable to save article");
- X fclose(fr);
- X }
- X }
- X /* Then redisplay the list */
- X display_page_again(line_no);
- X break;
- X
- X case '!':
- X printf( "\n" );
- X fgetstr("Shell cmd:",temp,shell_cmd_name,30,stdin);
- X if ( temp[0] )
- X {
- X strcpy( shell_cmd_name, temp );
- X system( temp );
- X printf( "Press any key to return to SLNR" );
- X fflush( stdout );
- X ch_get();
- X }
- X display_page_again(line_no);
- X break;
- X
- X
- X default:
- X redisplay=0;
- X break;
- X
- X }
- X }
- X return(0);
- }
- SHAR_EOF
- chmod 0440 slnr/slnr.c ||
- echo 'restore of slnr/slnr.c failed'
- Wc_c="`wc -c < 'slnr/slnr.c'`"
- test 39079 -eq "$Wc_c" ||
- echo 'slnr/slnr.c: original size 39079, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= slnr/slnr.doc ==============
- if test -f 'slnr/slnr.doc' -a X"$1" != X"-c"; then
- echo 'x - skipping slnr/slnr.doc (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting slnr/slnr.doc (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'slnr/slnr.doc' &&
- # SCCS header
- # File : slnr.doc
- # Version : 1.4
- X
- ===============================================================================
-
- X
- X _/_/_/_/ _/ _/ _/ _/_/_/_/_/
- X _/ _/ _/ _/_/ _/ _/ _/
- X _/ _/ _/ _/ _/ _/ _/
- X _/_/_/ _/ _/ _/ _/ _/_/_/_/_/
- X _/ _/ _/ _/ _/ _/ _/
- X _/ _/ _/ _/ _/ _/ _/ _/
- X _/_/_/ _/_/_/_/_/ _/ _/ _/ _/ _/
- X
- X Simple Local News Reader. V 2.1b (c) 1993 Philippe Goujard
- ===============================================================================
-
- X
- SLNR is a program to read Usenet news on your local machine,
- also called an off-line newsreader.
- X
- SLNR is THE off-line news reader that implements the SLNP format for
- exchanging electronic mail and usenet news messages between a usenet site
- (typically Unix) and your local machine.
- X
- The SLNP packet format is in the public domain and should be widely
- available. Check for file "slnp.fmt".
- X
- X
- X
- X
- Why SLNR ?
- ~~~~~~~~~~
- X - There is a growing market of people who want to read usenet news on
- X their local (dos/amiga/atari/mac) machine.
- X
- X - For the moment they have the choice between getting a "feed" from a unix
- X site and installing one of the uucp packages (waffle, fsuucp...) or
- X getting a tcp/ip connection. None of them is easy for beginners.
- X
- X - Many people know the bbs world and are familiar with QWK off-line mail
- X readers and want something similar.
- X
- Some vocabulary
- ~~~~~~~~~~~~~~~
- X The messages you are about to read are divided in two categories : your
- X private mail and usenet news messages (or posts).
- X
- X For people who come from the fidonet world this may be a little bit
- X confusing at first since on most fido bbs, "private mail" could be mixed
- X within public messages. Here private and public are totally separated.
- X
- X The news messages are grouped in "newsgroups" they are the equivalent of
- X what you can know as "mail area", "conference", "forum" or "continuum".
- X The groups are sorted in a hierarchy : for example all "comp.*" groups
- X are for computer subjects, all "rec.*" for recreational subjects. And
- X "comp.lang.*" will be all the computer langages etc...
- X
- X Most of those groups are shared among thousands of machines and millions
- X of readers in the world. This distribution is done by multiple ways,
- X some of it being by modem over phone line other being on dedicated fast
- X lines.
- X When you read a message on your machine there is a good chance that
- X several thousand other readers have done that already and that many
- X answers have already been posted.
- X
- X There are several rules that you should bear in mind when you want to
- X post a message to the net : those rules are called "nettiquette". It is
- X not within the scope of this documentation to expose the nettiquette but
- X if you are looking for pointers, have a look in newsgrpoups like :
- X "news.announce.newusers" "news.newsusers.questions" or "news.answers".
- X
- X
- 1) Installation & configuration
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- X To install SLNR you need to create a subdirectory where you put the
- X executable. You will later in that directory put the files extracted
- X from the SLNP packet.(*.MSG, AREAS, *.IDX etc...)
- X
- X You can also create in that directory a file called "sig.txt" this is
- X your signature that will be appended when you write a usenet message or
- X an electonic mail. Usually it should be one or two lines with you email
- X or postal address.
- X
- X The configuration file is called "slnr.ini". It is not mandatory to
- X have one. The configuration file contains lines of the form :
- X VARIABLE=value
- X where VARIABLE can be one of the following fields:
- X
- X Field : PAGER
- X Value : string
- X Default: See apendix B
- X Comment: This is the name of the external program that will be used to
- X display a message.
- X
- X Field : EDITOR
- X Value : string
- X Default: See apendix B
- X Comment: This is the name of the external program that will be used to
- X edit messages and posts.
- X
- X Field : COMPRESS
- X Value : string
- X Default: See apendix B
- X Comment: This is the name of the compactor program that will be used to
- X compress replies.
- X
- X Field : ANSI
- X Value : SET or UNSET
- X Default: SET
- X Comment: If SET, ansi color codes will be sent to the screen for the
- X MSDOS version (an ANSI.SYS or equivalent driver need to be loaded).
- X
- X Field : LINES
- X Value : Integer
- X Default: 24
- X Comment: This is the number of lines your screen can display. Under
- X MSDOS, changing this value does NOT put the screen in any specific
- X mode. This has to be done externally to SLNR.
- X
- X Field : QUOTE
- X Value : String
- X Default: "> "
- X Comment: This string will be added in front of mail answers or
- X follow-up messages in order to quote the message you are replying to.
- X Note that the double quotes are only mandatory if you want to include a
- X space character in the string.
- X
- X Example:
- X
- # This is the SLNR config file
- #
- # Each line starting with a hash is a comment
- #
- # The config file is not necessary, if not found SLNR will use the
- # defaults values.
- X
- EDITOR=C:\MSDOS\QEDIT
- PAGER=c:\tools\list
- X
- COMPRESS=pkzip
- X
- # Un-comment the following if you don't want ansi color codes
- # ANSI=No
- X
- X
- X
- X
- X
- 2) Starting the program
- ~~~~~~~~~~~~~~~~~~~~~~~
- X When you start SLNR and if the files "AREAS" and "xxx.MSG" are
- X present in the current directory you will see lines like this :
- X
- Reading Private_mail (12 articles) ..
- Reading biz.sco.general (18 articles) ..
- Reading biz.sco.opendesktop (6 articles) .
- Reading clari.sports.misc (1 articles) .
- Reading comp.bbs.waffle (20 articles) ..
- Reading comp.unix.sysv386 (28 articles) ...
- Reading control (10 articles) .
- Reading news.admin (2 articles) .
- Reading rec.arts.startrek.info (6 articles) .
- Reading rec.humor.funny (24 articles) ...
- Reading rec.humor.oracle (4 articles) .
- Reading uk.misc (190 articles) ...................
- X
- X The program prints the list of newsgroups found and for each newsgroup
- X prints a dot every 10 articles while it is reading them in memory.
- X
- X Note that in this example the first group is named "Private_mail" which
- X means that your unread email was selected as well as your newsgroups.
- X
- X
- 2) Reading Usenet Articles
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
- X When reading usenet articles you will be presented to a page that looks
- X like that :
- X
- X
- Group uk.misc <General interest to everyone on UKnet.> 190 articles
- X From Lines Subject
- ============================================================================
- X 1 G.S.Davies 8 Male or female name, help please?
-
- X 2 The Edible Dormo 22 Re: Mail complaint
-
- X 3 Chris Cooke 19 Re: Queen pays TAX, but there are SOME who don't.
-
- X 4 Pete Foulkes 22 Forged 1 pound coins was (Re: 10p coin)
-
- X 5 Pete Foulkes 38 Re: Getting robbed at Public Houses
-
- X 6 Graham Toal 9 Re: Male or female name, help please?
-
- X 7 Gary Fozzard 13 Bill and Ted's Bogus Journey
-
- X 8 Patrick Gosling 15 Re: Former England Captain
-
- X 9 Mark Turner 15 Re: Moving to UK from US...tips requested
-
- X 10 Brian D Milner 10 Broken Cash Register Dump ? ? ? ? ? ? ? ? ? ? ? ?
-
- X 11 Ian G Batten 8 Re: Is the police force useful?
-
- X 12 Brian D Milner 34 Dracula (Master, Master, everything is prepared !)
-
- X 13 James Petts 22 Re: Is the police force useful?
-
- X 14 Mark Porter 6 UK Inflation figs (6mths) reqd
-
- X 15 Roy Donaldson 25 Re: Car insurance group (again!)
-
- X 16 Tarang K Patel 37 Re: Michael Jackson -- I haven't had a face job!
-
- X 17 Tarang K Patel 114 Re: Queen pays TAX, but there are SOME who don't.
-
- X 18 Stephen R Cooper 150 Re: Queen pays TAX, but there are SOME who don't.
-
- X
- Again View Mail Write next_pg Next_grp Prev_grp Save Hlp Quit
- X
- X
- X
- X The first line contains the newsgroup name, it's description (if
- X available) between angle brackets and the number of article in
- X that newsgroup. Then the article list itself is divided into 4 columns:
- X
- X - The first column is the article number, to be referenced when using
- X the (V)iew (M)ail and (W)rite commands.
- X
- X - The second column is the author of the article, if the name of the
- X author cannot be found in the header then the email address is printed.
- X
- X - The third column is the number of lines in the article. Sometimes big
- X articles are interesting, but sometimes they are just quotes from
- X previous articles with "me too" added at the end : this is usenet.
- X
- X The last line displays the list of available commands (a command not
- X available does not appear in the list : for example here since we are
- X at the first page there is no "prev_pg" command).
- X
- X To call a command just type the first character of the command. (In the
- X color version, the first letter is of a different color).
- X Note that some commands are case sensitive ('n' and 'N', 'p' and 'P'
- X are different).
- X
- X - Again : Used to redisplay the current page again.
- X
- X - View : To view an article. This asks you for the article number and
- X calls the PAGER.
- X
- X - Mail : To send an electronic mail (either a new mail or an answer
- X to a message). This will be put in the "mail.rep" file to be uploaded.
- X
- X - Write : To post an article (either a new one or a follow-up to a
- X message). This will be put in the "articles.rep" file to be uploaded.
- X An article is posted in the current newsgroup. This command is not
- X available when the group is "Private_mail", your mailbox.
- X
- X - prev_pg : Previous page of the article list. This command is not
- X available if you are in the first page of the list. Note that this
- X command is case sensitive.
- X
- X - next_pg : Next page of the article list. This command is not
- SHAR_EOF
- true || echo 'restore of slnr/slnr.doc failed'
- fi
- echo 'End of part 2'
- echo 'File slnr/slnr.doc is continued in part 3'
- echo 3 > _shar_seq_.tmp
- exit 0
-
- --
- Philippe Goujard <Sysop> Email : pgoujard@infocom.co.uk
- INFOCOM : FREE (yes free!) Usenet access in the UK - (0734) 34 00 55
- For more information mail "info@infocom.co.uk" a daemon will autoreply
-
-